

module axi_master (
  
  aclk_i,
  areset_n_i,
  
  awid_o,
  awaddr_o,
  awlen_o,
  awsize_o,
  awburst_o,
  awlock_o,
  awvalid_o,
  awready_i,
  
  wid_o,
  wdata_o,
  wstrb_o,
  wlast_o,
  wvalid_o,
  wready_i,
  
  bid_i,
  bresp_i,
  bvalid_i,
  bready_o,
 
  arid_o,
  araddr_o,
  arlen_o,
  arsize_o,
  arburst_o,
  arlock_o,
  arvalid_o,
  arready_i,
  
  rid_i,
  rdata_i,
  rresp_i,
  rlast_i,
  rvalid_i,
  rready_o,
  
  write_start_i,
  write_awid_i,
  write_addr_i,
  write_length_i,
  write_size_i,
  write_burst_i,
  write_lock_i,
  write_wid_i,
  write_data_i,
  write_datav_i,
  write_strb_i,
  write_ack_o,
  write_data_req_o,
  write_done_o,
  write_err_o,
  write_bresp_o,
  write_bid_o,
  write_bvalid_o,
  
  read_start_i,
  read_arid_i,
  read_addr_i,
  read_length_i,
  read_size_i,
  read_burst_i,
  read_lock_i,
  read_af_i,
  read_ack_o,
  read_data_o,
  read_datav_o,
  read_err_o,
  read_done_o
);

  
  input           aclk_i;
  input           areset_n_i;
  
  output [3:0]    awid_o;
  output [31:0]   awaddr_o;
  output [3:0]    awlen_o;
  output [2:0]    awsize_o;
  output [1:0]    awburst_o;
  output [1:0]    awlock_o;
  output          awvalid_o;
  input           awready_i;
  
  output [3:0]    wid_o;
  output [127:0]  wdata_o;
  output [15:0]   wstrb_o;
  output          wlast_o;
  output          wvalid_o;
  input           wready_i;
  
  input  [3:0]    bid_i;
  input  [1:0]    bresp_i;
  input           bvalid_i;
  output          bready_o;
  
  output [3:0]    arid_o;
  output [31:0]   araddr_o;
  output [3:0]    arlen_o;
  output [2:0]    arsize_o;
  output [1:0]    arburst_o;
  output [1:0]    arlock_o;
  output          arvalid_o;
  input           arready_i;
  
  input  [3:0]    rid_i;
  input  [127:0]  rdata_i;
  input  [1:0]    rresp_i;
  input           rlast_i;
  input           rvalid_i;
  output          rready_o;
  
  input           write_start_i;
  input [3:0]     write_awid_i;
  input [31:0]    write_addr_i;
  input [3:0]     write_length_i;
  input [2:0]     write_size_i;
  input [1:0]     write_burst_i;
  input [1:0]     write_lock_i;
  input [3:0]     write_wid_i;
  input [127:0]   write_data_i;
  input           write_datav_i;
  input [15:0]    write_strb_i;
  output          write_ack_o;
  output          write_data_req_o;
  output          write_done_o;
  output          write_err_o;
  output [1:0]    write_bresp_o;
  output [3:0]    write_bid_o;
  output          write_bvalid_o;
  
  input           read_start_i;
  input [3:0]     read_arid_i;
  input [31:0]    read_addr_i;
  input [3:0]     read_length_i;
  input [2:0]     read_size_i;
  input [1:0]     read_burst_i;
  input [1:0]     read_lock_i;
  input           read_af_i;
  output          read_ack_o;
  output [127:0]  read_data_o;
  output          read_datav_o;
  output          read_err_o;
  output          read_done_o;

  reg             awvalid_o;
  reg [3:0]       wid_o;
  reg [127:0]     wdata_o;
  reg [15:0]      wstrb_o;
  reg             wlast_o;
  reg             wvalid_o;
  reg             arvalid_o;
  reg             rready_o;
  reg             write_ack_o;
  reg             write_data_req_o;
  reg             write_done_o;
  reg             write_err_o;
  reg             read_ack_o;
  reg [127:0]     read_data_o;
  reg             read_datav_o;
  reg             read_err_o;
  reg             read_done_o;

  wire [127:0] rdata_i;
  



  parameter       W_RESET_ST      = 0,
                  W_IDLE_ST       = 1,
                  W_GEN_VLD_ST    = 2,
                  W_CHK_RDY_ST    = 3,
                  W_CHK_BVLD_ST   = 4;
  
  parameter       R_RESET_ST       = 0,
                  R_IDLE_ST        = 1,
                  R_CHK_ARRDY      = 2,
                  R_WAIT_RLAST_ST  = 3;

  reg [2:0]       cur_state;
  reg [2:0]       nxt_state;
  reg [3:0]       xfer_pend_cnt;
  reg [3:0]       write_awid_reg;
  wire            data_avail_c;
  reg             write_datav_ltch;
  reg             nxt_wdata_req_pend;
  reg [3:0]       req_pend_cnt;
  reg [1:0]       rd_cur_state;
  reg [1:0]       rd_nxt_state;
  reg [3:0]       arid_reg;


  always @ (posedge aclk_i or negedge areset_n_i) begin
    if (areset_n_i == 1'b0)
      cur_state <= W_RESET_ST;
    else
      cur_state <= nxt_state;
  end


  always @ (cur_state or areset_n_i or write_start_i or wready_i or
            bvalid_i or xfer_pend_cnt or wvalid_o) begin
    case (cur_state)
      W_RESET_ST: begin
        if (areset_n_i == 1'b0)
          nxt_state = cur_state;
        else
          nxt_state = W_IDLE_ST;
      end

      W_IDLE_ST: begin
       if (write_start_i == 1'b1)
         nxt_state = W_GEN_VLD_ST;
       else
         nxt_state = cur_state;
      end

      W_GEN_VLD_ST: begin
        nxt_state = W_CHK_RDY_ST;
      end

      W_CHK_RDY_ST: begin
        if (wready_i == 1'b1 && wvalid_o == 1'b1 && xfer_pend_cnt == 4'd0)
          nxt_state = W_CHK_BVLD_ST;
        else
          nxt_state = cur_state;
      end

      W_CHK_BVLD_ST: begin
        if (bvalid_i == 1'b1 && bid_i == write_awid_reg)
          nxt_state = W_RESET_ST;
        else
          nxt_state = cur_state;
      end

      default:
        nxt_state = W_RESET_ST;
    endcase
  end



  assign awid_o         = write_awid_i;
  assign awaddr_o       = write_addr_i;
  assign awlen_o        = write_length_i;
  assign awsize_o       = write_size_i;
  assign awburst_o      = write_burst_i;
  assign awlock_o       = write_lock_i;
  assign write_bresp_o  = bresp_i;
  assign write_bid_o    = bid_i;
  assign write_bvalid_o = bvalid_i;



  always @ (posedge aclk_i or negedge areset_n_i) begin
    if (areset_n_i == 1'b0)
      awvalid_o <= 1'b0;
    else if (cur_state == W_IDLE_ST && write_start_i == 1'b1)
      awvalid_o <= 1'b1;
    else if (awready_i == 1'b1 || (cur_state == W_CHK_BVLD_ST && bvalid_i == 1'b1 && bid_i == write_awid_reg))
      awvalid_o <= 1'b0;
  end

  always @ (posedge aclk_i or negedge areset_n_i) begin
    if (areset_n_i == 1'b0)
      write_ack_o <= 1'b0;
    else if (awvalid_o == 1'b1 && awready_i == 1'b1)
      write_ack_o <= 1'b1;
    else
      write_ack_o <= 1'b0;
  end

  assign data_avail_c = write_datav_i | write_datav_ltch;


  always @ (posedge aclk_i or negedge areset_n_i) begin
    if (areset_n_i == 1'b0)
      xfer_pend_cnt <= 4'd0;
    else if (cur_state == W_IDLE_ST && write_start_i == 1'b1)
      xfer_pend_cnt <= write_length_i;
    else if (wvalid_o && wready_i && xfer_pend_cnt)
      xfer_pend_cnt <= xfer_pend_cnt - 4'd1;
  end
  
  always @ (posedge aclk_i or negedge areset_n_i) begin
    if (areset_n_i == 1'b0)
      req_pend_cnt <= 4'd0;
    else if (cur_state == W_IDLE_ST && write_start_i == 1'b1)
      req_pend_cnt <= write_length_i;
    else if (write_data_req_o)
      req_pend_cnt <= req_pend_cnt - 4'd1;
  end


  always @ (posedge aclk_i or negedge areset_n_i) begin
    if (areset_n_i == 1'b0)
      write_datav_ltch <= 1'b0;
    else if (write_datav_i && wvalid_o && !wready_i)
      write_datav_ltch <= 1'b1;
    else if (wready_i)
      write_datav_ltch <= 1'b0;
  end


  always @ (posedge aclk_i or negedge areset_n_i) begin
    if (areset_n_i == 1'b0)
      write_data_req_o  <= 1'b0;
    else if (cur_state == W_GEN_VLD_ST && req_pend_cnt != 4'd0)
      write_data_req_o  <= 1'b1;
    else if (((data_avail_c && wready_i && wvalid_o) ||
        (nxt_wdata_req_pend && write_datav_i)) && req_pend_cnt != 4'd0)
      write_data_req_o  <= 1'b1;
    else
      write_data_req_o  <= 1'b0;
  end
  
  always @ (posedge aclk_i or negedge areset_n_i) begin
    if (areset_n_i == 1'b0)
      nxt_wdata_req_pend <= 1'b0;
    else if (!data_avail_c & wready_i & wvalid_o && req_pend_cnt != 4'd0)// && !write_data_req_o)
      nxt_wdata_req_pend <= 1'b1;
    else if (write_datav_i)
      nxt_wdata_req_pend <= 1'b0;
  end

  always @ (posedge aclk_i or negedge areset_n_i) begin
    if (areset_n_i == 1'b0)
      write_awid_reg <= 4'd0;
    else if (cur_state == W_GEN_VLD_ST)
      write_awid_reg <= write_awid_i;
  end


  always @ (posedge aclk_i or negedge areset_n_i) begin
    if (areset_n_i == 1'b0)
      wvalid_o <= 1'b0;
    else if (cur_state == W_GEN_VLD_ST ||
             (cur_state == W_CHK_RDY_ST && data_avail_c == 1'b1))
      wvalid_o <= 1'b1;
    else if (cur_state == W_CHK_RDY_ST && wready_i == 1'b1)
      wvalid_o <= 1'b0;
  end
  
  always @ (posedge aclk_i or negedge areset_n_i) begin
    if (areset_n_i == 1'b0)
      wlast_o <= 1'b0;
    else if ((cur_state == W_GEN_VLD_ST && xfer_pend_cnt == 4'd0) ||
             (cur_state == W_CHK_RDY_ST && data_avail_c == 1'b1 && 
              ((wvalid_o == 1'b0 && xfer_pend_cnt == 4'd0) || (wvalid_o == 1'b1 && wready_i == 1'b1 && xfer_pend_cnt == 4'd1))))
      wlast_o <= 1'b1;
    else if (cur_state == W_CHK_RDY_ST && wready_i == 1'b1)
      wlast_o <= 1'b0;
  end
  
  
  always @ (posedge aclk_i or negedge areset_n_i) begin
    if (areset_n_i == 1'b0)
      wdata_o <= 128'd0;
    else if ((cur_state == W_GEN_VLD_ST ) ||
             ((cur_state == W_CHK_RDY_ST && data_avail_c == 1'b1) && 
              ((wvalid_o == 1'b0) || (wvalid_o == 1'b1 && wready_i == 1'b1)))
            )
      wdata_o <= write_data_i;
  end

  always @ (posedge aclk_i or negedge areset_n_i) begin
    if (areset_n_i == 1'b0)
      wstrb_o <= 16'd0;
    else if ((cur_state == W_GEN_VLD_ST ) ||
             ((cur_state == W_CHK_RDY_ST && data_avail_c == 1'b1) && 
              ((wvalid_o == 1'b0) || (wvalid_o == 1'b1 && wready_i == 1'b1)))
            )
      wstrb_o <= write_strb_i;
  end
  
   
  always @ (posedge aclk_i or negedge areset_n_i) begin
    if (areset_n_i == 1'b0)
      wid_o <= 4'd0;
    else if ((cur_state == W_GEN_VLD_ST ) ||
             ((cur_state == W_CHK_RDY_ST && data_avail_c == 1'b1) && 
              ((wvalid_o == 1'b0) || (wvalid_o == 1'b1 && wready_i == 1'b1)))
            )
      wid_o <= write_wid_i;
  end

  assign bready_o = (cur_state ==W_CHK_BVLD_ST)? 1'b1 : 1'b0;

  always @ (posedge aclk_i or negedge areset_n_i) begin
    if (areset_n_i == 1'b0)
      write_done_o <= 1'b0;
    else if (cur_state == W_CHK_BVLD_ST && bvalid_i == 1'b1 && bid_i == write_awid_reg)
      write_done_o <= 1'b1;
    else
      write_done_o <= 1'b0;
  end

  always @ (posedge aclk_i or negedge areset_n_i) begin
    if (areset_n_i == 1'b0)
      write_err_o <= 1'b0;
    else if (cur_state == W_CHK_BVLD_ST && bvalid_i == 1'b1 && bid_i == write_awid_reg)
             
      write_err_o <= (bresp_i != 2'd0)? 1'b1:awvalid_o;
    else
      write_err_o <= 1'b0;
  end


  always @ (posedge aclk_i or negedge areset_n_i) begin
    if (areset_n_i == 1'b0)
      rd_cur_state <= R_RESET_ST;
    else
      rd_cur_state <= rd_nxt_state;
  end


  always @ (*) begin
    case (rd_cur_state)
      R_RESET_ST: begin
        if (areset_n_i == 1'b1)
          rd_nxt_state = R_IDLE_ST;
        else
          rd_nxt_state = rd_cur_state;
      end

      R_IDLE_ST: begin
        if (read_start_i == 1'b1)
          rd_nxt_state = R_CHK_ARRDY;
        else
          rd_nxt_state = rd_cur_state;
      end

      R_CHK_ARRDY: begin
        if (arready_i == 1'b1)
          rd_nxt_state = R_WAIT_RLAST_ST;
        else
          rd_nxt_state = rd_cur_state;
      end

      R_WAIT_RLAST_ST: begin

        if (rlast_i == 1'b1 && rready_o == 1'b1)
          rd_nxt_state = R_IDLE_ST;
        else
          rd_nxt_state = rd_cur_state;
      end

      default:
        rd_nxt_state = R_RESET_ST;

    endcase
  end


  always @ (posedge aclk_i or negedge areset_n_i) begin
    if (areset_n_i == 1'b0)
      arvalid_o <= 1'b0;
    else if (rd_cur_state == R_IDLE_ST && read_start_i == 1'b1)
      arvalid_o <= 1'b1;
    else if (arready_i == 1'b1)
      arvalid_o <= 1'b0;
  end

  always @ (posedge aclk_i or negedge areset_n_i) begin
    if (areset_n_i == 1'b0)
      rready_o <= 1'b1;
    else
      rready_o <= ~read_af_i;
  end

  always @ (posedge aclk_i or negedge areset_n_i) begin
    if (areset_n_i == 1'b0)
      arid_reg <= 1'b0;
    else if (arvalid_o == 1'b1 && arready_i == 1'b1)
      arid_reg <= read_arid_i;
  end

  always @ (posedge aclk_i or negedge areset_n_i) begin
    if (areset_n_i == 1'b0)
      read_ack_o <= 1'b0;
    else if (arvalid_o == 1'b1 && arready_i == 1'b1)
      read_ack_o <= 1'b1;
    else
      read_ack_o <= 1'b0;
  end

  always @ (posedge aclk_i or negedge areset_n_i) begin
    if (areset_n_i == 1'b0)
      read_data_o <= 128'd0;
    else
      read_data_o <= rdata_i;
  end
  
  always @ (posedge aclk_i or negedge areset_n_i) begin
    if (areset_n_i == 1'b0)
      read_datav_o <= 1'b0;

    else if (rd_cur_state == R_WAIT_RLAST_ST && rvalid_i == 1'b1 && rready_o == 1'b1)
      read_datav_o <= 1'b1;
    else
      read_datav_o <= 1'b0;
  end

  always @ (posedge aclk_i or negedge areset_n_i) begin
    if (areset_n_i == 1'b0)
      read_done_o <= 1'b0;

    else if (rd_cur_state == R_WAIT_RLAST_ST && rlast_i == 1'b1 && rready_o == 1'b1)
      read_done_o <= 1'b1;
    else
      read_done_o <= 1'b0;
  end

  always @ (posedge aclk_i or negedge areset_n_i) begin
    if (areset_n_i == 1'b0)
      read_err_o <= 1'b0;

    else if (rd_cur_state == R_WAIT_RLAST_ST && rvalid_i == 1'b1 && rready_o == 1'b1)
                                  
      read_err_o  <= (rresp_i != 2'b00)?1'b1:1'b0;
    else
      read_err_o <= 1'b0;
  end

  assign arid_o      = read_arid_i;
  assign araddr_o    = read_addr_i;
  assign arlen_o     = read_length_i;
  assign arsize_o    = read_size_i;
  assign arburst_o   = read_burst_i;
  assign arlock_o    = read_lock_i;
  
  


endmodule

